/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is ART Ontology API (Sesame2 Implementation).
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2007.
 * All Rights Reserved.
 *
 * ART Ontology API (Sesame2 Implementation) was developed by the Artificial Intelligence Research Group
 * (art.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about the ART Ontology API (Sesame2 Implementation) can be obtained at 
 * http//art.uniroma2.it/owlart
 *
 */
package it.uniroma2.art.owlart.sesame2impl.models;

import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelCreationException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.exceptions.UnsupportedQueryLanguageException;
import it.uniroma2.art.owlart.exceptions.UnsupportedRDFFormatException;
import it.uniroma2.art.owlart.io.RDFFormat;
import it.uniroma2.art.owlart.io.RDFSerializer;
import it.uniroma2.art.owlart.model.ARTBNode;
import it.uniroma2.art.owlart.model.ARTLiteral;
import it.uniroma2.art.owlart.model.ARTNode;
import it.uniroma2.art.owlart.model.ARTResource;
import it.uniroma2.art.owlart.model.ARTStatement;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.models.BaseRDFTripleModel;
import it.uniroma2.art.owlart.models.RDFSReasoner;
import it.uniroma2.art.owlart.models.TransactionBasedModel;
import it.uniroma2.art.owlart.models.impl.BaseRDFModelImpl;
import it.uniroma2.art.owlart.navigation.ARTNamespaceIterator;
import it.uniroma2.art.owlart.navigation.ARTResourceIterator;
import it.uniroma2.art.owlart.navigation.ARTStatementIterator;
import it.uniroma2.art.owlart.navigation.RDFIterator;
import it.uniroma2.art.owlart.query.BooleanQuery;
import it.uniroma2.art.owlart.query.GraphQuery;
import it.uniroma2.art.owlart.query.MalformedQueryException;
import it.uniroma2.art.owlart.query.Query;
import it.uniroma2.art.owlart.query.QueryLanguage;
import it.uniroma2.art.owlart.query.TupleQuery;
import it.uniroma2.art.owlart.resources.Resources;
import it.uniroma2.art.owlart.sesame2impl.Sesame2ARTResourceFactory;
import it.uniroma2.art.owlart.sesame2impl.io.RDFFormatConverter;
import it.uniroma2.art.owlart.sesame2impl.navigation.Sesame2ARTNamespaceIteratorImpl;
import it.uniroma2.art.owlart.sesame2impl.navigation.Sesame2ARTResourceIteratorImpl;
import it.uniroma2.art.owlart.sesame2impl.navigation.Sesame2ARTStatementIteratorImpl;
import it.uniroma2.art.owlart.sesame2impl.query.BooleanQuerySesame2Impl;
import it.uniroma2.art.owlart.sesame2impl.query.GraphQuerySesame2Impl;
import it.uniroma2.art.owlart.sesame2impl.query.QueryLanguageConverter;
import it.uniroma2.art.owlart.sesame2impl.query.TupleQuerySesame2Impl;
import it.uniroma2.art.owlart.utilities.RDFIterators;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.openrdf.model.BNode;
import org.openrdf.model.Namespace;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.RDFWriter;
import org.openrdf.sail.SailException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Sesame2 Implementation of the basic RDF access class in OWL ART</br></br>
 * 
 * note for when inference is activated on read methods (list..., get.. etc...), and the interpretaton of
 * these methods with respect to Sesame2 behavior
 * 
 * <ul>
 * <li>the presence of a single ANY or MAINGRAPH makes the method ignore other graphs and globally return an
 * array containing <em>only</em> the null context (as the null context with inference set to true contains
 * all the triples in the store)</li>
 * <li>if no graph is specified, for the same reasons above, an array with the single null context is returned
 * </li>
 * </ul>
 * 
 * @author Armando Stellato <a href="mailto:stellato@info.uniroma2.it">stellato@info.uniroma2.it</a>
 * 
 */
public class BaseRDFModelSesame2Impl extends BaseRDFModelImpl implements RDFModelSesame2, BaseRDFTripleModel,
		RDFSReasoner, TransactionBasedModel, RDFSerializer {

	protected static Logger logger = LoggerFactory.getLogger(BaseRDFModelSesame2Impl.class);

	protected Sesame2ARTResourceFactory ses2artFact;

	protected Repository localrepository;
	// protected SailConnection conn;
	protected RepositoryConnection repConn;
	protected ValueFactory vF;
	protected String baseURI;
	protected String defaultNamespace;

	protected boolean rdfsReasoning;

	public BaseRDFModelSesame2Impl(Repository repo, boolean rdfsReasoning) throws SailException,
			RepositoryException, ModelCreationException {

		logger.info("initializing Sesame2 repository...");
		this.localrepository = repo;

		this.rdfsReasoning = rdfsReasoning;

		/*
		 * try { conn = localrepository.getSail().getConnection(); } catch(SailException e) { throw new
		 * ModelCreationException(e.getMessage()); }
		 */
		logger.info("getting Connection...");
		try {
			repConn = localrepository.getConnection();
		} catch (RepositoryException e) {
			throw new ModelCreationException(e.getMessage());
		}
		logger.info("autocommit is not being set... (defaults to true; see changelog of version 1.0.3) ");
		// repConn.setAutoCommit(true);

		logger.info("initializing value factory...");
		vF = repo.getValueFactory();

		ses2artFact = new Sesame2ARTResourceFactory(vF);

		logger.info("BaseRDFModel Sesame2 repository initialized...");
	}

	public RepositoryConnection getSesame2RepositoryConnection() {
		return repConn;
	}

	public void addStatement(ARTStatement stat, ARTResource... graphs) throws ModelUpdateException {
		try {
			repConn.add(ses2artFact.aRTStatement2SesameStatement(stat), getSesameContextsForADD(graphs));
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		}
	}

	public void addTriple(ARTResource subject, ARTURIResource predicate, ARTNode object,
			ARTResource... graphs) throws ModelUpdateException {
		try {
			repConn.add(ses2artFact.aRTResource2SesameResource(subject),
					ses2artFact.aRTURIResource2SesameURI(predicate), ses2artFact.aRTNode2SesameValue(object),
					getSesameContextsForADD(graphs));
			// logger.debug("adding triple: " + subject + "|" + predicate + "|" + object + "|" +
			// (graphs.length>0?"context0: "+graphs[0]:"nocontext"));
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		}
	}

	public void addRDF(File inputFile, String baseURI, RDFFormat rdfFormat, ARTResource... graphs)
			throws FileNotFoundException, IOException, ModelAccessException, ModelUpdateException,
			UnsupportedRDFFormatException {
		try {
			repConn.add(inputFile, baseURI, RDFFormatConverter.convert(rdfFormat),
					getSesameContextsForADD(graphs));
		} catch (RDFParseException e) {
			logger.error("" + e);
			throw new IOException(e);
		} catch (RepositoryException e) {
			logger.error("" + e);
			throw new ModelUpdateException(e);
		}
	}

	public void addRDF(URL url, String baseURI, RDFFormat rdfFormat, ARTResource... graphs)
			throws FileNotFoundException, IOException, ModelAccessException, ModelUpdateException,
			UnsupportedRDFFormatException {
		org.openrdf.rio.RDFFormat sesame2RDFformat = null;
		try {
			sesame2RDFformat = (rdfFormat != null) ? RDFFormatConverter.convert(rdfFormat) : null;
			logger.debug("rdf format chosen: " + sesame2RDFformat);
			repConn.add(url, baseURI, sesame2RDFformat, getSesameContextsForADD(graphs));
		} catch (RDFParseException e) {
			if (sesame2RDFformat == null) {
				// the second attempt (with another format) should be made only in case the RDFFormat was not
				// specified.
				logger.info("sesame2 rdfparse exception"
						+ e
						+ ", maybe the format cannot be inferred and the default sesame2 format is not ok for: "
						+ baseURI + ", trying now rdfxml format");
				try {
					repConn.add(url, baseURI, org.openrdf.rio.RDFFormat.RDFXML,
							getSesameContextsForADD(graphs));
				} catch (RDFParseException e1) {
					logger.error("" + e1);
					throw new IOException(e1);
				} catch (RepositoryException e1) {
					logger.error("" + e1);
					throw new ModelUpdateException(e1);
				}
			} else {
				logger.error("" + e);
				throw new IOException(e);
			}
		} catch (RepositoryException e) {
			logger.error("" + e);
			throw new ModelUpdateException(e);
		}
	}

	public void deleteTriple(ARTResource subject, ARTURIResource property, ARTNode object,
			ARTResource... graphs) throws ModelUpdateException {
		try {
			repConn.remove(ses2artFact.aRTResource2SesameResource(subject),
					(URI) ses2artFact.aRTResource2SesameResource(property),
					ses2artFact.aRTNode2SesameValue(object), getSesameContextsForDELETE(graphs));
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		}

	}

	public void deleteStatement(ARTStatement statement, ARTResource... graphs) throws ModelUpdateException {
		try {
			repConn.remove(ses2artFact.aRTResource2SesameResource(statement.getSubject()),
					(URI) ses2artFact.aRTResource2SesameResource(statement.getPredicate()),
					ses2artFact.aRTNode2SesameValue(statement.getObject()),
					getSesameContextsForDELETE(graphs));
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e.getMessage());
		}
	}

	// this method implementation first checks if this is an OWL Model (we do not use instanceof since the
	// base repository typically encapsulated in an OWLModel is not an OWLModel).
	// then it clears all the data and then rechecks if the owl is still present (it may have been deleted
	// due to the graphs specified in graphs). If it is not, it reloads it
	public void clearRDF(ARTResource... graphs) throws ModelUpdateException {
		try {
			boolean isOWL = false;
			ARTURIResource owl = this.createURIResource(OWL.NAMESPACE);
			Collection<ARTResource> ngs = RDFIterators.getCollectionFromIterator(this.listNamedGraphs());
			if (ngs.contains(owl))
				isOWL = true;
			logger.debug("repository originally contained the OWL named graph: " + isOWL);

			repConn.clear(getSesameContextsForDELETE(graphs));

			ngs = RDFIterators.getCollectionFromIterator(this.listNamedGraphs());
			if (isOWL && !ngs.contains(this.createURIResource(OWL.NAMESPACE))) {
				logger.debug("repository does not contain anymore the OWL named graph, reloading it");
				this.addRDF(Resources.class.getResource("owl.rdfs"), OWL.NAMESPACE, RDFFormat.RDFXML, owl);
			}

		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		} catch (ModelAccessException e) {
			throw new ModelUpdateException(e);
		} catch (FileNotFoundException e) {
			throw new ModelUpdateException(e);
		} catch (IOException e) {
			throw new ModelUpdateException(e);
		} catch (UnsupportedRDFFormatException e) {
			throw new ModelUpdateException(e);
		}
	}

	public String getBaseURI() {
		return baseURI;
	}

	public String getDefaultNamespace() {
		return defaultNamespace;
	}

	public ARTStatementIterator listStatements(ARTResource subj, ARTURIResource pred, ARTNode obj,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		RepositoryResult<Statement> repRes;
		try {
			repRes = repConn.getStatements(ses2artFact.aRTResource2SesameResource(subj),
					ses2artFact.aRTURIResource2SesameURI(pred), ses2artFact.aRTNode2SesameValue(obj),
					inferred, getSesameContextsForREAD(inferred, graphs));
			return new Sesame2ARTStatementIteratorImpl(repRes);
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	public boolean hasTriple(ARTResource subj, ARTURIResource pred, ARTNode obj, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {

		try {
			return repConn.hasStatement(ses2artFact.aRTResource2SesameResource(subj),
					(URI) ses2artFact.aRTURIResource2SesameURI(pred), ses2artFact.aRTNode2SesameValue(obj),
					inferred, getSesameContextsForREAD(inferred, graphs));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	public boolean hasStatement(ARTStatement stat, boolean inferred, ARTResource... graphs)
			throws ModelAccessException {
		try {
			return repConn.hasStatement(ses2artFact.aRTStatement2SesameStatement(stat), inferred,
					getSesameContextsForREAD(inferred, graphs));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	public ARTResourceIterator listNamedGraphs() throws ModelAccessException {
		RepositoryResult<Resource> nsIt;
		try {
			nsIt = repConn.getContextIDs();
			return (new Sesame2ARTResourceIteratorImpl(nsIt));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	public ARTNamespaceIterator listNamespaces() throws ModelAccessException {
		RepositoryResult<Namespace> nsIt;
		try {
			nsIt = repConn.getNamespaces();
			return (new Sesame2ARTNamespaceIteratorImpl(nsIt));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}

	}

	public void setBaseURI(String uri) throws ModelUpdateException {
		this.baseURI = uri;
	}

	public void setDefaultNamespace(String namespace) throws ModelUpdateException {
		this.defaultNamespace = namespace;
		setNsPrefix(namespace, "");
	}

	public void writeRDF(File outputFile, RDFFormat rdfFormat, ARTResource... graphs) throws IOException,
			ModelAccessException, UnsupportedRDFFormatException {
		FileOutputStream fos = new FileOutputStream(outputFile);
		RDFWriter writer = RDFFormatConverter.getWriterFactory(rdfFormat).getWriter(fos);

		try {
			// this operation is not a write-on-repository operation, but it writes
			repConn.export(writer, getSesameContextsForREAD(false, graphs));
		} catch (RepositoryException e) {
			logger.error("" + e);
			throw new ModelAccessException(e);
		} catch (RDFHandlerException e) {
			logger.error("" + e);
			throw new IOException(e);
		}
	}

	public void writeRDF(OutputStream os, RDFFormat rdfFormat, ARTResource... graphs) throws IOException,
			ModelAccessException, UnsupportedRDFFormatException {
		RDFWriter writer = RDFFormatConverter.getWriterFactory(rdfFormat).getWriter(os);

		try {
			repConn.export(writer, getSesameContextsForADD(graphs));
		} catch (RepositoryException e) {
			logger.error("" + e);
			throw new ModelAccessException(e);
		} catch (RDFHandlerException e) {
			logger.error("" + e);
			throw new IOException(e);
		}
	}

	public void writeRDF(RDFIterator<ARTStatement> it, RDFFormat format, OutputStream out)
			throws ModelAccessException, UnsupportedRDFFormatException, IOException {
		while (it.streamOpen()) {
			RDFWriter writer = RDFFormatConverter.getWriterFactory(format).getWriter(out);
			try {
				Map<String, String> prefixMap = getNamespacePrefixMapping();
				for (Entry<String, String> entry : prefixMap.entrySet()) {
					writer.handleNamespace(entry.getKey(), entry.getValue());
				}
				writer.startRDF();
				while (it.streamOpen()) {
					writer.handleStatement(ses2artFact.aRTStatement2SesameStatement(it.getNext()));
				}
				writer.endRDF();
			} catch (RDFHandlerException e) {
				throw new IOException(e);
			}
		}
		it.close();
	}

	public void writeRDF(RDFIterator<ARTStatement> it, RDFFormat format, Writer wout)
			throws ModelAccessException, UnsupportedRDFFormatException, IOException {
		while (it.streamOpen()) {
			RDFWriter writer = RDFFormatConverter.getWriterFactory(format).getWriter(wout);
			try {
				Map<String, String> prefixMap = getNamespacePrefixMapping();
				for (Entry<String, String> entry : prefixMap.entrySet()) {
					writer.handleNamespace(entry.getKey(), entry.getValue());
				}
				writer.startRDF();
				while (it.streamOpen()) {
					writer.handleStatement(ses2artFact.aRTStatement2SesameStatement(it.getNext()));
				}
				writer.endRDF();
			} catch (RDFHandlerException e) {
				throw new IOException(e);
			}
		}
		it.close();
	}

	/*
	 * *************************************** FROM PrefixMapping Interface
	 * **************************************
	 */

	// TODO STARRED check if Sesame 2 has support for such methods
	public String expandQName(String qname) throws ModelAccessException {
		if (qname == null)
			throw new IllegalAccessError("qname to be expanded cannot be null!");
		if (maybeIsURI(qname))
			return qname;
		String[] parts = qname.split(":");
		if (parts.length == 1)
			return getDefaultNamespace() + qname;
		else
			return getNSForPrefix(parts[0]) + parts[1];
	}

	// TODO URGENT!!! THIS IS TO DISCOVER QNAME WHICH ARE
	// ALREADY URI. but...put better method for
	// discovering if the qame is an uri!!!
	private boolean maybeIsURI(String input) {
		if (input.contains("#") || input.contains("/"))
			return true;
		return false;
	}

	public String getNSForPrefix(String prefix) throws ModelAccessException {
		try {
			return repConn.getNamespace(prefix);
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	public Map<String, String> getNamespacePrefixMapping() throws ModelAccessException {
		Map<String, String> res = new HashMap<String, String>();
		RepositoryResult<Namespace> resN;
		try {
			resN = repConn.getNamespaces();
			while (resN.hasNext()) {
				Namespace nm = resN.next();
				res.put(nm.getPrefix(), nm.getName());
			}
			resN.close();
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
		return res;
	}

	public String getPrefixForNS(String namespace) throws ModelAccessException {
		try {
			RepositoryResult<Namespace> res = repConn.getNamespaces();
			while (res.hasNext()) {
				Namespace nm = res.next();
				if (nm.getName().equalsIgnoreCase(namespace))
					return nm.getPrefix();
			}
			res.close();
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
		return null;
	}

	public String getQName(String uri) throws ModelAccessException {
		URI realURI = vF.createURI(uri);
		String namespace = realURI.getNamespace();
		if (namespace.equals(defaultNamespace))
			return realURI.getLocalName();
		String prefix = getPrefixForNS(namespace);
		if (prefix == null)
			return uri;
		else
			return prefix + ":" + realURI.getLocalName();
	}

	public void removeNsPrefixMapping(String namespace) throws ModelUpdateException {
		try {
			String prefix = getPrefixForNS(namespace);
			repConn.removeNamespace(prefix);
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		} catch (ModelAccessException e) {
			throw new ModelUpdateException(e);
		}
	}

	public void setNsPrefix(String namespace, String prefix) throws ModelUpdateException {
		try {
			String prevPrefix = getPrefixForNS(namespace);
			if (prevPrefix != null) {
				System.err.println("a mapping already exists for " + namespace + ", removing it");
				repConn.removeNamespace(prevPrefix);
			}
			repConn.setNamespace(prefix, namespace);
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		} catch (ModelAccessException e) {
			throw new ModelUpdateException(e);
		}
	}

	/*
	 * *************************************** Resource Creation methods
	 * **************************************
	 */

	public ARTLiteral createLiteral(String literalString) {
		return ses2artFact.sesameLiteral2ARTLiteral(vF.createLiteral(literalString));
	}

	public ARTLiteral createLiteral(String literalString, String language) {
		return ses2artFact.sesameLiteral2ARTLiteral(vF.createLiteral(literalString, language));
	}

	public ARTLiteral createLiteral(String literalString, ARTURIResource datatype) {
		return ses2artFact.sesameLiteral2ARTLiteral(vF.createLiteral(literalString,
				ses2artFact.aRTURIResource2SesameURI(datatype)));
	}

	public ARTURIResource createURIResource(String uri) {
		URI newURI = vF.createURI(uri);
		return ses2artFact.sesameURI2ARTURIResource(newURI);
	}

	public ARTStatement createStatement(ARTResource subject, ARTURIResource predicate, ARTNode object) {
		return ses2artFact.sesameStatement2ARTStatement(vF.createStatement(
				ses2artFact.aRTResource2SesameResource(subject),
				ses2artFact.aRTURIResource2SesameURI(predicate), ses2artFact.aRTNode2SesameValue(object)));
	}

	public void close() throws ModelUpdateException {
		try {
			logger.info("closing owlModel");
			repConn.close();
			// conn.close();
			localrepository.shutDown();
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		}
		/*
		 * catch (SailException e) { throw new ModelUpdateException(e); }
		 */
	}

	public ARTBNode createBNode() {
		BNode bNode = vF.createBNode();
		return ses2artFact.sesameBNode2ARTBNode(bNode);
	}

	public ARTBNode createBNode(String ID) {
		BNode bNode = vF.createBNode(ID);
		return ses2artFact.sesameBNode2ARTBNode(bNode);
	}

	/*
	 * *************************************** QUERY Methods **************************************
	 */

	public BooleanQuery createBooleanQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		try {
			return new BooleanQuerySesame2Impl(repConn.prepareBooleanQuery(
					QueryLanguageConverter.convert(ql), query, baseURI));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		} catch (org.openrdf.query.MalformedQueryException e) {
			throw new MalformedQueryException(e);
		}
	}

	public GraphQuery createGraphQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		try {
			return new GraphQuerySesame2Impl(repConn.prepareGraphQuery(QueryLanguageConverter.convert(ql),
					query, baseURI));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		} catch (org.openrdf.query.MalformedQueryException e) {
			throw new MalformedQueryException(e);
		}
	}

	public Query createQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		try {
			org.openrdf.query.Query ses2query = repConn.prepareQuery(QueryLanguageConverter.convert(ql),
					query, baseURI);
			if (ses2query instanceof org.openrdf.query.TupleQuery) {
				return new TupleQuerySesame2Impl((org.openrdf.query.TupleQuery) ses2query);
			} else if (ses2query instanceof org.openrdf.query.BooleanQuery)
				return new BooleanQuerySesame2Impl((org.openrdf.query.BooleanQuery) ses2query);
			else if (ses2query instanceof org.openrdf.query.GraphQuery)
				return new GraphQuerySesame2Impl((org.openrdf.query.GraphQuery) ses2query);
			else
				throw new ModelAccessException("unknown query type");
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		} catch (org.openrdf.query.MalformedQueryException e) {
			logger.error("malformed query");
			throw new MalformedQueryException(e);
		}
	}

	public TupleQuery createTupleQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		try {
			return new TupleQuerySesame2Impl(repConn.prepareTupleQuery(QueryLanguageConverter.convert(ql),
					query, baseURI));
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		} catch (org.openrdf.query.MalformedQueryException e) {
			throw new MalformedQueryException(e);
		}
	}

	/*
	 * *************************************** Local Utility Methods **************************************
	 */

	/**
	 * maingraph is transformed into the sesame2 null graph<br/>
	 * use of ANY throws an exception as it is not allowed in add operations
	 * 
	 * @param graphs
	 * @return
	 */
	private Resource[] getSesameContextsForADD(ARTResource... graphs) {
		if (graphs == null)
			throw new IllegalArgumentException(
					"a null value cannot be accepted in place of a list of named graphs");
		Resource[] resContexts = new Resource[graphs.length];
		for (int i = 0; i < graphs.length; i++) {
			if (graphs[i] == null)
				throw new IllegalArgumentException("a null value cannot be accepted as a named graph");
			// a null value for a contest is not accepted in OWLArt
			// (though in Sesame it means: null-context), null-context (main unnamed graph) in OWLArt must be
			// specified as MAINGRAPH
			else if (graphs[i] == NodeFilters.ANY)
				throw new IllegalArgumentException(
						"the ANY Node can be used only when reading/deleting triples from the model, not for writing");
			else if (graphs[i] == NodeFilters.MAINGRAPH) {
				resContexts[i] = null; // in Sesame, MAINGRAPH is implemented with null on the graphs
			}

			else
				resContexts[i] = ses2artFact.aRTResource2SesameResource(graphs[i]);
		}
		return resContexts;
	}

	/**
	 * maingraph is transformed into the sesame2 null graph<br/>
	 * use of ANY ignores other graphs and returns the empty array (which means "all contexts" in sesame)
	 * 
	 * @param graphs
	 * @return
	 */
	private Resource[] getSesameContextsForDELETE(ARTResource... graphs) {
		if (graphs == null)
			throw new IllegalArgumentException(
					"a null value cannot be accepted in place of a list of named graphs");
		Resource[] resContexts = new Resource[graphs.length];
		for (int i = 0; i < graphs.length; i++) {
			if (graphs[i] == null)
				throw new IllegalArgumentException("a null value cannot be accepted as a named graph");
			// a null value for a contest is not accepted in OWLArt
			// (though in Sesame it means: null-context), null-context (main unnamed graph) in OWLArt must be
			// specified as MAINGRAPH
			else if (graphs[i] == NodeFilters.ANY)
				// if any of the graphs is ANY, then an empty array is sent to Sesame (which interpretes as
				// all graphs when reading/deleting and maingraph when adding triples)
				return new Resource[0];

			else if (graphs[i] == NodeFilters.MAINGRAPH) {
				resContexts[i] = null; // in Sesame, MAINGRAPH is implemented with null on the graphs
			}

			else
				resContexts[i] = ses2artFact.aRTResource2SesameResource(graphs[i]);
		}
		return resContexts;
	}

	/**
	 * when there is no inference
	 * <ul>
	 * <li>MAINGRAPH is transformed into the sesame2 null graph</li>
	 * <li>the presence of a single ANY makes the method ignore other graphs and globally return the empty
	 * array (which means "all contexts" in sesame)</li>
	 * </ul>
	 * 
	 * when inference is activated
	 * <ul>
	 * <li>the presence of a single ANY or MAINGRAPH makes the method ignore other graphs and globally return
	 * the an array containing <em>only</em> the null context (as the null context with inference set to true
	 * contains all the triples in the store)</li>
	 * <li>if no graph is specified, for the same reasons above, an array with the single null context is
	 * returned</li>
	 * </ul>
	 * 
	 * 
	 * @param inference
	 * @param graphs
	 * @return
	 */
	private Resource[] getSesameContextsForREAD(boolean inference, ARTResource... graphs) {
		// TODO make the withInference behaviour selectable through OWLART API, like with an option where the
		// user can decide if literally respecting the named graphs list or if "no repetitions" should be
		// guaranteed at the cost of shifting the graphs (but still respecting the triples to be asked, that
		// is, the maingraph can be returned in place of specific graphs if maingraph or any are selected)

		if (graphs == null)
			throw new IllegalArgumentException();
		if (inference && (graphs.length == 0)) {
			// in case of inference returns the sole nullContext, which will be read only one time
			// and contains all the triples in the store
			return createNullContext();
		}

		Resource[] resContexts = new Resource[graphs.length];
		for (int i = 0; i < graphs.length; i++) {
			if (graphs[i] == null)
				throw new IllegalArgumentException("a null value cannot be accepted as a named graph");
			// a null value for a contest is not accepted in OWLArt
			// (though in Sesame it means: null-context), null-context (main unnamed graph) in OWLArt must be
			// specified as MAINGRAPH
			else if (graphs[i] == NodeFilters.ANY)
				// if any of the graphs is ANY, then an empty array is sent to Sesame (which interpretes as
				// all graphs when reading/deleting and maingraph when adding triples)
				// TODO could be implemented, when writing, as a special NodeFilter/graphs which always fails
				// the equals
				if (inference) {
					// in case of inference returns the sole nullContext, which will be read only one time
					// and contains all the triples in the store
					return createNullContext();
				} else
					return new Resource[0];
			else if (graphs[i] == NodeFilters.MAINGRAPH) {
				if (inference) {
					// in case of inference returns the sole nullContext, which will be read only one time
					// and contains all the triples in the store
					return createNullContext();
				} else
					resContexts[i] = null; // in Sesame, MAINGRAPH is implemented with null on the graphs
			}

			else
				resContexts[i] = ses2artFact.aRTResource2SesameResource(graphs[i]);
		}
		return resContexts;
	}

	private Resource[] createNullContext() {
		Resource[] nullContext = new Resource[1];
		nullContext[0] = null;
		return nullContext;
	}

	public boolean supportsSubPropertyMaterialization() {
		// true if the forwardChaining Sail is put in the sail stack in the configuration in
		// ARTModelFactorySesame2Impl
		return rdfsReasoning;
	}

	public boolean supportsSubPropertyOfClosure() {
		// true if the forwardChaining Sail is put in the sail stack in the configuration in
		// ARTModelFactorySesame2Impl
		return rdfsReasoning;
	}

	public boolean supportsSubClassOfClosure() {
		// true if the forwardChaining Sail is put in the sail stack in the configuration in
		// ARTModelFactorySesame2Impl
		return rdfsReasoning;
	}

	public boolean supportsClassIdentification() {
		// true if the rdfsReasoning Sail is put in the sail stack in the configuration in
		// ARTModelFactorySesame2Impl
		return rdfsReasoning;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.TransactionBasedModel#commit()
	 */
	public void commit() throws ModelUpdateException {
		try {
			repConn.commit();
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.TransactionBasedModel#isAutoCommit()
	 */
	public boolean isAutoCommit() throws ModelAccessException {
		try {
			return repConn.isAutoCommit();
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.TransactionBasedModel#rollBack()
	 */
	public void rollBack() throws ModelAccessException {
		try {
			repConn.rollback();
		} catch (RepositoryException e) {
			throw new ModelAccessException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.TransactionBasedModel#setAutoCommit()
	 */
	public void setAutoCommit(boolean value) throws ModelUpdateException {
		try {
			repConn.setAutoCommit(value);
		} catch (RepositoryException e) {
			throw new ModelUpdateException(e);
		}
	}

}
